﻿Imports Microsoft.Win32

'todo: consider adding a new page that shows something like: http://timeanddate.com/worldclock/meetingtime.html?month=2&day=6&year=2009&p1=134&p2=237&p3=240&p4=136&iv=0
'todo: consider adding capability to default the location information for meeting request.
'todo: sort timezones in drop down list box similar to the time zones box in control panel
'todo: add ability to use the last timezone webservice that responded.  store usage statistics on the webservices so that
'       the code can pick the best one to use overtime.  perhaps a default, option. which one to use first.
'todo: sometimes an attendee change event is fired after the user has click on the send button.  If this is the case
'           ideally would not do any updates and bypass the code to update the grid because the window is going to be closed.
'           this is especially annoyning when it prompts you for a timezone.
'todo: xml file is used to store timezone associations, is there a better place/way to do this.
'todo: provide support for localization
'todo: registry to define rest webservice for looking up timezone
'todo: Ideally we want the region to show up on the Scheduling page of the appointment.
'       currently form regions don't support multiple pages.  Watch for future releases
'       of regions that may support this capability.
'todo: Ideally we don't want region to display when there are no entries
'       form region's don't support this.  A custom task pane could accomplish
'       this though programming is more complicated.  Watch for future release's
'       of form regions to support hiding the region programmatically.
'todo: handle corrupted xml file
'todo: error handling on web services could be better
' Terms: 
'       TZMap - is the locally stored association of location to a time zone.
'       Location - is the combination of the City, and state for the attendee or organization of the appointment
'
' For creating the setup program I referenced the following article
'http://msdn.microsoft.com/en-us/library/cc563937.aspx


Public Class InternationalTimes
    Friend WithEvents currentApppointment As Outlook.AppointmentItem
    Shared WithEvents cApp As Outlook.AppointmentItem

    ' dsTZMap is dataset that is used to maintain the association of a location with a timezone
    '   the TZMap is stored in the location: System.Windows.Forms.Application.LocalUserAppDataPath & "\" & "TZMap.xml"
    Shared dsTZMap As System.Data.DataSet

    ' dtAvailableTimeZones is the dataset containing the list of available time zones that has been 
    ' defined on the computer running the add-in
    Shared dtAvailableTimeZones As System.Data.DataTable

    '    Dim WithEvents NoLocationAttendees As New System.Windows.Forms.ContextMenuStrip
    Dim _Cell As System.Windows.Forms.DataGridViewCell
#Region "Form Region Factory"

    <Microsoft.Office.Tools.Outlook.FormRegionMessageClass(Microsoft.Office.Tools.Outlook.FormRegionMessageClassAttribute.Appointment)> _
    <Microsoft.Office.Tools.Outlook.FormRegionName("ShowInternationalTimes.InternationalTimes")> _
    Partial Public Class InternationalTimesFactory

        ' Occurs before the form region is initialized.
        ' To prevent the form region from appearing, set e.Cancel to true.
        ' Use e.OutlookItem to get a reference to the current Outlook item.
        Private Sub InternationalTimesFactory_FormRegionInitializing(ByVal sender As Object, ByVal e As Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs) _
        Handles Me.FormRegionInitializing
            cApp = e.OutlookItem
            'Dim x As InternationalTimes = DirectCast(sender, InternationalTimes)
            'x.currentApppointment = e.OutlookItem

            dsTZMap = GetLocationsTimeZones()
        End Sub
    End Class

#End Region

    'Occurs before the form region is displayed. 
    'Use Me.OutlookItem to get a reference to the current Outlook item.
    'Use Me.OutlookFormRegion to get a reference to the form region.

    Private Sub InternationalTimes_FormRegionShowing(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles MyBase.FormRegionShowing
        Try

            Me.currentApppointment = cApp
            tbStatus.Visible = False
            ' Configure headers on grid
            dgvTimes.Columns("dgvStartTime").HeaderText = currentApppointment.Start.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
            dgvTimes.Columns("dgvEndTime").HeaderText = currentApppointment.End.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
            dgvTimes.ColumnHeadersDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(231, 239, 255)
            dgvTimes.EnableHeadersVisualStyles = False

            ' Minimize region if nothing to display
            'todo: resize the control if nothing is displayed
            'Dim frc As WindowFormRegionCollection
            'frc = Globals.FormRegions(Globals.ThisAddIn.Application.ActiveInspector)

            'frc.InternationalTimes.dgvTimes.Visible = False
            'Me.OutlookFormRegion.Reflow()

            ' Populate DropDown with available timezones
            Dim tz As TimeZoneInfo
            Dim col As System.Windows.Forms.DataGridViewComboBoxColumn
            Dim rec(2) As String

        

            dtAvailableTimeZones = New System.Data.DataTable
            dtAvailableTimeZones.Columns.Add("DisplayName")
            dtAvailableTimeZones.Columns.Add("ValueName")
            dtAvailableTimeZones.Columns.Add("offset")
        

            For Each tz In TimeZoneInfo.GetSystemTimeZones
                rec(0) = tz.DisplayName
                rec(1) = tz.Id
                rec(2) = tz.BaseUtcOffset.Hours
                dtAvailableTimeZones.Rows.Add(rec)
            Next

            col = Me.dgvTimes.Columns("dgvTimeZone")
            col.DataSource = dtAvailableTimeZones
            col.DisplayMember = "DisplayName"
            col.ValueMember = "ValueName"
            DisplayTimeZones(My.Settings.SuggestTZReading)
            
        Catch ex As Exception
            ShowError(ex.Message, ex.StackTrace)
        End Try

    End Sub


    Private Sub InternationalTimes_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize

        'Me.dgvTimes.Width = cApp.Application.ActiveWindow.width - 2 'Me.Width - 2

        dgvTimes.Columns("dgvTimeZone").Width = (cApp.Application.ActiveWindow.width - 525)
        If dgvTimes.Columns("dgvTimeZone").Width < 300 Then
            dgvTimes.Columns("dgvTimeZone").Width = 300
        End If


    End Sub

    


    'Private Sub dgvTimes_CellValueChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvTimes.CellValueChanged
    Private Sub dgvTimes_CellEndEdit(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
        Handles dgvTimes.CellEndEdit

        If e.RowIndex >= 0 Then
            ' If the user edited a time zone setting for a location update the TZMap 
            If dgvTimes.Columns(e.ColumnIndex).Name = "dgvTimeZone" Then
                ' Determine if Location is in the Time Zone Map, ie. has a corresponding time zone
                '    and update the time zone information in the map with the value that was just edited
                Dim dr As System.Data.DataRow
                Dim found = False
                For Each dr In dsTZMap.Tables("TZMap").Rows
                    'todo: in theory these should never be null, but it was observed during testing
                    If Not dr.IsNull("Location") Then

                        If dr.Item("Location") = dgvTimes.Rows(e.RowIndex).Cells("dgvLocation").Value Then
                            dr.Item("TimeZone") = dgvTimes.Rows(e.RowIndex).Cells("dgvTimeZone").Value
                            found = True
                        End If

                    End If

                Next

                If found = False Then
                    dr = dsTZMap.Tables("TZMap").Rows.Add
                    dr.BeginEdit()
                    dr.Item("Location") = dgvTimes.Rows(e.RowIndex).Cells("dgvLocation").Value
                    dr.Item("TimeZone") = dgvTimes.Rows(e.RowIndex).Cells("dgvTimeZone").Value
                    dr.EndEdit()
                End If
            End If
            Try
                Dim temp As DateTime
                Dim tz As String
                tz = dgvTimes.Rows(e.RowIndex).Cells("dgvTimeZone").Value
                If Not (tz Is Nothing) Then
                    temp = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentApppointment.Start, tz)
                    dgvTimes.Rows(e.RowIndex).Cells("dgvStartTime").Value = temp.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
                    dgvTimes.Rows(e.RowIndex).Cells("dgvStartDate").Value = temp.Date.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern)
                    temp = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentApppointment.End, dgvTimes.Rows(e.RowIndex).Cells("dgvTimeZone").Value)
                    dgvTimes.Rows(e.RowIndex).Cells("dgvEndDate").Value = temp.Date.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern)
                    dgvTimes.Rows(e.RowIndex).Cells("dgvEndTime").Value = temp.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
                End If
            Catch ex As Exception
                ShowError(ex.Message, ex.StackTrace)
            End Try
            'Note: decided to save the time zone file here as each time zone is applied. Rather than 
            '       saving the file on form close.  The cost is relatively low to do here and if a user
            '       makes a change in an appointment and then starts another apointment the time zone will
            '       show up in the new appointment.  If we move this to form close then the above case
            '       does not work.
            Try

            Catch ex As Exception

            End Try
            Try
                SaveLocationsTimeZones(dsTZMap)
                'dsTZMap.WriteXml(System.Windows.Forms.Application.LocalUserAppDataPath & "\" & "TZMap.xml")
            Catch ex As Exception
                ShowError(ex.Message, ex.StackTrace)
            End Try
        End If
    End Sub
    Private Sub dgvTimes_DataError(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewDataErrorEventArgs) Handles dgvTimes.DataError
        ShowError("Error with the data populating the international times grid. ", _
                   "column:" & e.ColumnIndex & vbCrLf & _
                   "row:" & e.RowIndex & vbCrLf & _
                   "context: " & e.Context & vbCrLf & _
                   "exception: " & e.Exception.Message & vbCrLf & System.Environment.StackTrace)
        dgvTimes.Item(e.ColumnIndex, e.RowIndex).Value = dtAvailableTimeZones.Rows(0).Item("DisplayName")




    End Sub
   

    Private Sub currentAppointment_PropertyChange(ByVal Name As String) _
    Handles currentApppointment.PropertyChange

        Select Case Name
            Case "RequiredAttendees"
                DisplayTimeZones()
            Case "OptionalAttendees"
                DisplayTimeZones()
            Case "StartUTC"
                HandleDateTimeChange()
            Case "EndUTC"
                HandleDateTimeChange()
        End Select
    End Sub


    Sub ResetTZMap()
        dsTZMap.Clear()
        dsTZMap = DataXML()
    End Sub

    Sub HandleDateTimeChange()
        Dim i As Integer
        Dim tz As String
        Try
            For i = 0 To Me.dgvTimes.Rows.Count - 1

                tz = dgvTimes.Rows(i).Cells("dgvTimeZone").Value
                If tz <> "" Then
                    'tz = LookupTZIDFromDisplayTZ(tz)
                    dgvTimes.Columns("dgvStartTime").HeaderText = currentApppointment.Start.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
                    dgvTimes.Columns("dgvEndTime").HeaderText = currentApppointment.End.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)

                    Dim temp As DateTimeOffset
                    temp = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentApppointment.Start, tz)

                    dgvTimes.Rows(i).Cells("dgvStartTime").Value = temp.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
                    dgvTimes.Rows(i).Cells("dgvStartDate").Value = temp.Date.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern)
                    temp = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentApppointment.End, tz)
                    dgvTimes.Rows(i).Cells("dgvEndDate").Value = temp.Date.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern)
                    dgvTimes.Rows(i).Cells("dgvEndTime").Value = temp.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)

                End If

            Next
        Catch ex As Exception
            ShowError(ex.Message, ex.StackTrace)
        End Try
    End Sub
    Sub WarnNoLocation(ByVal attendee As Outlook.Recipient)
        Dim t As System.Windows.Forms.ToolStripItem
        Me.tbStatus.Visible = True
        If Me.NoLocationAttendees.Items.Count = 0 Then
            t = NoLocationAttendees.Items.Add("No Location information for ")

            t.Enabled = False
        End If
        Dim found As Boolean = False
        For i = 0 To NoLocationAttendees.Items.Count - 1
            If NoLocationAttendees.Items(i).Text = attendee.Name Then
                found = True
                Exit For
            End If
        Next
        If found = False Then
            t = NoLocationAttendees.Items.Add(attendee.Name)
            t.Tag = attendee


        End If


    End Sub
    Function LocationDisplayed(ByVal attendeeLocation As Location) As Boolean
        Dim i As Integer
        Dim found As Boolean = False
        For i = 0 To Me.dgvTimes.Rows.Count - 1
            If Me.dgvTimes.Rows(i).Cells(0).Value = attendeeLocation.key Then
                found = True
                Exit For
            End If
        Next
        LocationDisplayed = found
    End Function
    Sub DisplayLocation(ByVal attendeeLocation As Location, ByVal promptForTZ As Boolean)
        Dim rec(5) As String
        Dim tz As String

        Try
            For i = 0 To 5
                rec(i) = ""
            Next
            rec(0) = attendeeLocation.key
            tz = LookupTZinTZmap(attendeeLocation.key)
            If (tz <> "") And (tz <> System.TimeZoneInfo.Local.Id) Then
                Try
                    'tz = LookupTZIDFromDisplayTZ(tz)
                    Dim temp As DateTimeOffset
                    temp = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentApppointment.Start, tz)
                    rec(1) = temp.Date.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern)
                    rec(2) = temp.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
                    temp = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentApppointment.End, tz)
                    rec(3) = temp.Date.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern)
                    rec(4) = temp.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortTimePattern)
                Catch ex As Exception
                    ShowError(ex.Message, ex.StackTrace)
                End Try
                rec(5) = tz

            End If
            If (tz <> System.TimeZoneInfo.Local.Id) Then
                'todo: not sure why I need to make this check.  Apparently we are somehow getting into this
                '  code area when dgvtimes has not been initialized but I am not sure how that is happening
                '  the work around below seems to work.
                If dgvTimes.Columns.Count > 1 Then

                    If tz = "" Then
                        If (My.Settings.SuggestTZ = True) And (promptForTZ = True) Then
                            Dim f As New SelectTZ
                            Dim s As String = ""
                            s = f.ShowDialog(dtAvailableTimeZones, attendeeLocation.city, attendeeLocation.state, _
                                             attendeeLocation.country, currentApppointment.Application)
                            If s <> "" Then
                                rec(5) = s
                                ' Update the cache with the new TZ Map entry
                                Dim dr As System.Data.DataRow
                                dr = dsTZMap.Tables("TZMap").Rows.Add
                                dr.BeginEdit()
                                dr.Item("Location") = rec(0)
                                dr.Item("TimeZone") = rec(5)
                                dr.EndEdit()
                                SaveLocationsTimeZones(dsTZMap)
                                'dsTZMap.WriteXml(System.Windows.Forms.Application.LocalUserAppDataPath & "\" & "TZMap.xml")
                            End If
                        End If

                    End If
                    attendeeLocation = Nothing
                    Me.dgvTimes.Rows.Add(rec)
                    dgvTimes.ClearSelection()

                    HandleDateTimeChange()
                End If
            End If
        Catch ex As Exception
            ShowError(ex.Message, ex.StackTrace)
        End Try
    End Sub
    Sub DisplayTimeZones(Optional ByVal promptForTZ As Boolean = True)
        Dim attendee As Outlook.Recipient
        Dim attendeeLocation As Location

        Try
            For Each attendee In currentApppointment.Recipients
                If attendee.Resolved = True Then
                    attendeeLocation = New Location(attendee)
                    If attendeeLocation.exist = False Then
                        WarnNoLocation(attendee)
                    Else
                        If LocationDisplayed(attendeeLocation) = False Then
                            DisplayLocation(attendeeLocation, promptForTZ)
                        End If
                    End If
                End If
            Next
        Catch ex As Exception
            ShowError(ex.Message, ex.StackTrace)
        End Try
    End Sub


    Shared Function LookupTZinTZmap(ByVal CityState As String) As String
        Dim dr As System.Data.DataRow
        Dim result As String = ""

        For Each dr In dsTZMap.Tables("TZMap").Rows
            If Not dr.IsNull("Location") Then
                If dr.Item("Location") = CityState Then
                    result = dr.Item("TimeZone").ToString
                    Exit For
                End If
            End If
        Next
        LookupTZinTZmap = result
    End Function
    Shared Function DataXML() As System.Data.DataSet
        Dim ds As New System.Data.DataSet
        Dim fsReadXML As System.IO.FileStream = Nothing
        Dim filename As String
        Try

            filename = System.Windows.Forms.Application.LocalUserAppDataPath & "\" & "TZMap.xml"
            If True = Microsoft.VisualBasic.FileIO.FileSystem.FileExists(filename) Then
                fsReadXML = New System.IO.FileStream(filename, System.IO.FileMode.Open)
                ds.ReadXml(fsReadXML)
                If TableExist(ds, "TZMap") = False Then
                    ds.Tables.Add("TZMap")
                End If
                If ds.Tables("TZMap").Columns.Contains("Location") = False Then
                    ds.Tables("TZMap").Columns.Add("Location")
                End If
                If ds.Tables("TZMap").Columns.Contains("TimeZone") = False Then
                    ds.Tables("TZMap").Columns.Add("TimeZone")
                End If
            Else
                Dim dt As System.Data.DataTable
                dt = ds.Tables.Add("TZMap")
                dt.Columns.Add("Location")
                dt.Columns.Add("TimeZone")
            End If

        Catch ex As Exception
            ShowError(ex.Message, ex.StackTrace)
        Finally
            If Not (fsReadXML Is Nothing) Then
                fsReadXML.Close()
            End If
            DataXML = ds
        End Try
    End Function
    Shared Sub SaveLocationsTimeZones(ByRef ds As System.Data.DataSet)
        Dim dr As System.Data.DataRow
        Dim rootValue As String = "HKEY_CURRENT_USER\Software\Outlook2007DIT\Locations"
        Dim rowId As Integer = 1
        Try
            Dim s(1) As String


            For Each dr In ds.Tables("TZMap").Rows
                s(0) = dr.Item("Location")
                s(1) = dr.Item("TimeZone")
                My.Computer.Registry.SetValue(rootValue, rowId, s)
                rowId = rowId + 1
            Next

            'remove any previously defined locations
            Dim finished As Boolean = False
            Dim keyValue As String
            Do Until finished = True

                Try
                    My.Computer.Registry.GetValue(rootValue, rowId, "")
                    keyValue = rootValue & "\" & rowId
                    Using key As RegistryKey = My.Computer.Registry.LocalMachine.OpenSubKey(keyValue)
                        key.DeleteSubKey(keyValue)
                    End Using

                Catch ex As Exception
                    finished = True
                End Try
                rowId = rowId + 1
            Loop
        Catch ex As Exception
            ShowError(ex.Message, ex.StackTrace)
        End Try
        
    End Sub
    Shared Function GetLocationsTimeZones() As System.Data.DataSet
        Dim ds As New System.Data.DataSet
        Dim dt As System.Data.DataTable
        Dim finished As Boolean = False
        Dim rootValue As String = "HKEY_CURRENT_USER\Software\Outlook2007DIT\Locations"
        Dim keyValue() As String
        Dim rowId As Integer = 1
        Dim exists As Boolean = False

        Try
            dt = ds.Tables.Add("TZMap")
            dt.Columns.Add("Location")
            dt.Columns.Add("TimeZone")

            Do Until finished = True

                Try
                    keyValue = My.Computer.Registry.GetValue(rootValue, rowId, "")
                    ds.Tables("TZMap").Rows.Add(keyValue)
                    exists = True
                Catch ex As Exception
                    finished = True
                End Try
                rowId = rowId + 1
            Loop

        Catch ex As Exception
            ShowError(ex.Message, ex.StackTrace)
        Finally

            GetLocationsTimeZones = ds
        End Try
    End Function
    Shared Function TableExist(ByRef ds As System.Data.DataSet, ByVal tableName As String) As Boolean
        Dim i As Integer
        Dim result As Boolean = False
        For i = 0 To ds.Tables.Count - 1
            If ds.Tables(i).TableName = tableName Then
                result = True
                Exit For

            End If
        Next
        TableExist = result
    End Function
    Shared Sub ShowError(ByVal condition As String, ByVal stackTrace As String)
        Dim se As New ErrorDialog
        Dim t As System.Windows.Forms.DialogResult

        t = se.Show(condition, stackTrace)
        If t = System.Windows.Forms.DialogResult.Yes Then
            Dim m As Outlook.MailItem
            m = cApp.Application.CreateItem(Outlook.OlItemType.olMailItem)
            m.To = "bradyguy@live.com"
            m.Subject = "Outlook 2007 DIT Error Report"
            m.Body = My.Resources.reportErrorPreamble & vbCrLf & condition & vbCrLf & stackTrace
            m.Display(True)
        End If
    End Sub


    Private Sub tbStatus_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles tbStatus.Click
        NoLocationAttendees.Show(tbStatus, 0, 0)

    End Sub


    Private Sub bSetting_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles bSetting.Click
        Dim f As New Options
        If f.ShowDialog() = True Then
            ResetTZMap()

        End If
    End Sub

    Private Sub NoLocationAttendees_ItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles NoLocationAttendees.ItemClicked
        Dim contact As Outlook.Recipient

        contact = e.ClickedItem.Tag
        contact.AddressEntry.Details()

    End Sub

    
    Private Sub dgvTimes_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvTimes.EditingControlShowing
        'http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridviewcomboboxeditingcontrol.aspx
        Dim combo As System.Windows.Forms.ComboBox = Nothing

        Try
            combo = CType(e.Control, System.Windows.Forms.ComboBox)

        Catch ex As Exception
            System.Diagnostics.Debug.Print(ex.Message)
        End Try

        If (combo IsNot Nothing) Then

            ' Remove an existing event-handler, if present, to avoid 
            ' adding multiple handlers when the editing control is reused.
            RemoveHandler combo.SelectedIndexChanged, _
                New EventHandler(AddressOf ComboBox_SelectedIndexChanged)

            ' Add the event handler. 
            AddHandler combo.SelectedIndexChanged, _
                New EventHandler(AddressOf ComboBox_SelectedIndexChanged)
        End If
    End Sub
    Private Sub ComboBox_SelectedIndexChanged( _
        ByVal sender As Object, ByVal e As EventArgs)

        Dim comboBox1 As System.Windows.Forms.ComboBox = CType(sender, System.Windows.Forms.ComboBox)
        dgvTimes.CurrentCell.Value = comboBox1.SelectedValue
        Me.HandleDateTimeChange()
    End Sub
End Class
